home *** CD-ROM | disk | FTP | other *** search
/ Programming Sound Cards / Programming Sound Cards.iso / sound_87 / playmod.pas < prev    next >
Pascal/Delphi Source File  |  1995-01-01  |  44KB  |  1,293 lines

  1. {****************************************************************************}
  2. {                                                                            }
  3. { MODULE:         PlayMod                                                    }
  4. {                                                                            }
  5. { DESCRIPTION:    This UNIT allows to play a music module (*.MOD) in any     }
  6. {                 device supported in the SoundDevices sound system.         }
  7. {                                                                            }
  8. {                 Entrys:   PlayMod To begin playing the MOD.                }
  9. {                           StopMod To stop playing the MOD.                 }
  10. {                                                                            }
  11. { AUTHOR:         Juan Carlos Arévalo                                        }
  12. {                 Luis Crespo (parts extracted from the JAMP 1.5 MOD Player) }
  13. {                                                                            }
  14. { MODIFICATIONS:  Nobody (yet... ;-)                                         }
  15. {                                                                            }
  16. { HISTORY:        22-Jun-1992 Begins to use the SoundDevices sound system.   }
  17. {                             Internal cleaning, which was quite needed.     }
  18. {                             UnCanal routine made even faster.              }
  19. {                 11-Nov-1992 Redocumentation. There have been really many   }
  20. {                             enhancements since June, but they weren't      }
  21. {                             documented. Mainly more speed-ups.             }
  22. {                 24-Jan-1993 Added 8 voice support.                         }
  23. {                                                                            }
  24. {                                                                            }
  25. { (C) 1992 VangeliSTeam                                                      }
  26. {____________________________________________________________________________}
  27.  
  28. UNIT PlayMod;
  29.  
  30. INTERFACE
  31.  
  32. USES SongUnit, SongUtils, SongElements, ModCommands, SoundDevices, Filters, Kbd;
  33.  
  34.  
  35.  
  36.  
  37. { Definitions. }
  38.  
  39. TYPE
  40.   TTickProc = PROCEDURE(VAR Song: TSong; note: BOOLEAN); { Procedure to execute every tick. }
  41.   TVolumes  = ARRAY[1..MaxChannels] OF BYTE;             { Volume set (all channels). }
  42.  
  43.  
  44.  
  45.  
  46. { General definitions about the way of playing the music. }
  47. { Music player configuration.                             }
  48.  
  49. CONST
  50.   PlayingSong        : PSong         = NIL;
  51.   LoopMod            : BOOLEAN       = TRUE;   { TRUE if music can be played forever.                  }
  52.   ForceLoopMod       : BOOLEAN       = FALSE;  { TRUE if music must be played forever.                 }
  53.   CanFallBack        : BOOLEAN       = TRUE;   { TRUE if fall-back is allowed.                         }
  54.   FilterOn           : TFilterMethod = fmNone; { Initial value of the ON  filter.                      }
  55.   FilterOff          : TFilterMethod = fmNone; { Initial value of the OFF filter.                      }
  56.   FilterIsOn         : BOOLEAN       = FALSE;  { Initial position of the filter (FALSE = OFF).         }
  57.   TicksPerSecond     : WORD          = 50;     { Number of ticks per second, 50 = Europe, 60 = USA.    }
  58.   MaxOutputFreq      : WORD          = 44000;  { Maximum frequency of the output sound.                }
  59.                                                { Less means less memory for buffers.                   }
  60.  
  61.  
  62. VAR
  63.   SplBuf             : ARRAY[1..MaxChannels] OF WORD;
  64.  
  65.                                                              
  66. { Exported variables. }
  67.  
  68. CONST
  69.   Playing          : BOOLEAN = FALSE;       { (Read only) TRUE if the music is sounding right now.     }
  70.   ModTickProcValid : BOOLEAN = FALSE;       { TRUE if the module tick procedure has been initialised.  }
  71.  
  72. VAR
  73.   ActualHz        : WORD;                   { Desired freq. of the sound.                              }
  74.   NoteHz          : WORD;                   { Freq. to be used in the current tick.                    }
  75.   UserVols        : TVolumes;               { Channel volumes.                                         }
  76.   Permisos        : ARRAY[1..MaxChannels] OF BOOLEAN; { Permissions for playing the channels.          }
  77.   TickCount       : WORD;                   { Ticks counter. Increments each tick.                     }
  78.   ModTickProc     : TTickProc;              { Tick procedure. A procedure to be executed every tick.   }
  79.   MyCanFallBack   : BOOLEAN;                { Actual permission to fall-back.                          }
  80.   FilterVal       : TFilterMethod;          { Method of the filter to be used.                         }
  81.                                                              
  82.  
  83. { Definition of the local stack. }
  84.  
  85. CONST
  86.   PlayModStackSize = 500;   { Size of the stack. }
  87.  
  88. VAR
  89.   PlayModStack     : ARRAY[1..PlayModStackSize] OF BYTE;     
  90.  
  91.  
  92. { Definitions concerning a note. Buffer of the last N notes. }
  93.  
  94. TYPE
  95.   PPlayingNote = ^TPlayingNote;
  96.   TPlayingNote = RECORD
  97.     EoMod       : BOOLEAN;                 { TRUE if it is the note following the last.                }
  98.     Tempo       : BYTE;                    { Number of ticks the note will last.                       }
  99.     NotePlaying : BYTE;                    { Index of the note inside the pattern.                     }
  100.     SeqPlaying  : BYTE;                    { Sequence number of the pattern to which the note belongs. }
  101.     Volume      : TVolumes;                { Volumes of the channels.                                  }
  102.     Note        : ARRAY[1..MaxChannels] OF TFullNote; { Notes of the channels.                       }
  103.     NMuestras   : WORD;                    { Number of samples processed for each note.                }
  104.     fill        : BYTE;                    { To make it a 32-byte record.                              }
  105.   END;
  106.  
  107. CONST
  108.   NoteBuffSize = 1;   { Number of note buffers. }
  109.  
  110. VAR
  111.   NoteBuff      : ARRAY[0..NoteBuffSize-1] OF TPlayingNote;
  112.  
  113. CONST
  114.   NoteTl        : WORD = 0;
  115.   NoteHd        : WORD = 0;
  116.  
  117.   NoteSound     : PPlayingNote = NIL;
  118.   NoteProcessed : PPlayingNote = NIL;
  119.  
  120. VAR
  121.   Canales : ARRAY[1..MaxChannels] OF TCanal; { State of the channels. }
  122.  
  123.  
  124.  
  125.  
  126. {----------------------------------------------------------------------------}
  127. { Definition of the buffers where the samples are placed.                    }
  128. {____________________________________________________________________________}
  129.  
  130. CONST
  131.   MaxSplPerTick : WORD = 880; { Maximum samples in the buffer. Means maximum samples per tick. }
  132.   NumBuffers           = 3;   { Number of buffers.                                             }
  133.  
  134. VAR
  135.   BuffIdx,         { Tail of the buffer. }
  136.   BuffGive : WORD; { Head of the buffer. }
  137.  
  138.   Buffers       : ARRAY[1..NumBuffers] OF TSampleBuffer;
  139.   SizeOfABuffer : WORD;
  140.  
  141.  
  142.  
  143. {----------------------------------------------------------------------------}
  144. { Exported procedures.                                                       }
  145. {____________________________________________________________________________}
  146.  
  147. PROCEDURE PlayStart(VAR Song: TSong);
  148. PROCEDURE PlayStop;
  149.  
  150. PROCEDURE ChangeSamplingRate(Hz: WORD);
  151. PROCEDURE ProcessTickEntry;
  152. PROCEDURE FillWithSamples   (VAR Buff; Size: WORD);
  153.  
  154.  
  155.  
  156.  
  157. IMPLEMENTATION
  158.  
  159. USES Dos,
  160.      Heaps,
  161.      Debugging;
  162.  
  163.  
  164.  
  165.  
  166. {----------------------------------------------------------------------------}
  167. { General definitions of the module player. They define its actual state.    }
  168. {____________________________________________________________________________}
  169.  
  170. VAR
  171.   DelaySamples    : BOOLEAN;     { TRUE means it couldn't fill the samples buffer.                }
  172.   MuestrasPerTick : WORD;        { Number of samples that there are in a tick at the actual freq. }
  173.  
  174.  
  175.  
  176.  
  177. {----------------------------------------------------------------------------}
  178. { Raw channel definitions.                                                   }
  179. {____________________________________________________________________________}
  180.  
  181. TYPE
  182.   PModRawChan = ^TModRawChan;
  183.   TModRawChan = RECORD
  184.     Flags      : BYTE;         { Channel flags (see below).                 }
  185.  
  186.     SplPosFrac : BYTE;         { Position fraction.                         }
  187.     SplPosInt  : WORD;         { Position offset.                           }
  188.     SplPosSeg  : WORD;         { Position segment.                          }
  189.                                
  190.     SplOfs     : WORD;         { Actual sample part offset.                 }
  191.     SplSeg     : WORD;         { Actual sample part segment.                }
  192.     SplLimit   : WORD;         { Actual sample part size.                   }
  193.  
  194.     SplOfs1    : WORD;         { First  sample part offset.                 }
  195.     SplSeg1    : WORD;         { First  sample part segment.                }
  196.     SplLimit1  : WORD;         { First  sample part size.                   }
  197.  
  198.     SplOfs2    : WORD;         { Second sample part offset.                 }
  199.     SplSeg2    : WORD;         { Second sample part segment.                }
  200.     SplLimit2  : WORD;         { Second sample part size.                   }
  201.  
  202.     StepFrac   : BYTE;         { Sample incement fraction.                  }
  203.     StepInt    : WORD;         { Sample incement integer.                   }
  204.  
  205.     Volume     : BYTE;         { Volume to be used.                         }
  206.  
  207.     LoopEnd    : WORD;         { Offset of the end of the loop in its part. }
  208.     LoopLen    : WORD;         { Size of the loop in its part.              }
  209.   END;
  210.  
  211. CONST                      { TModRawChan.Flags }
  212.   rcfLongSample     = $01; { Set if it's a long (more than 65520 bytes) sample.     }
  213.   rcfActiveChannel  = $02; { Set if the channel is activated (permission to sound). }
  214.   rcfDoesLoop       = $04; { Set of the sample has a loop.                          }
  215.   rcfPlaying2nd     = $08; { Set if playing the 2nd part of the long loop.          }
  216.   rcfLongLoopLen    = $10; { Loop size goes from the 2nd part to the 1st.           }
  217.   rcfLongLoopEnd    = $20; { Loop ends in the 2nd part.                             }
  218.   rcfSampleFinished = $40; { Set if the sample has already finished.                }
  219.  
  220. VAR                                                 { Raw channels. }
  221.   RawChannels : ARRAY[1..MaxChannels] OF TModRawChan;
  222.  
  223.  
  224.  
  225.  
  226. {----------------------------------------------------------------------------}
  227. { Basic, fast assembler routines.                                            }
  228. {____________________________________________________________________________}
  229.  
  230.  
  231.  
  232.  
  233. {----------------------------------------------------------------------------}
  234. {                                                                            }
  235. { ROUTINE: UnCanal                                                           }
  236. {                                                                            }
  237. { Fills a buffer with 8 bit samples, calculated from a sample, a freq. and   }
  238. { a volume (a RawChannel).                                                   }
  239. { Implemented as several specialised routines, for speed's sake.             }
  240. { It doesn't play long samples yet.                                          }
  241. { This routine self-modifies, for speed's sake.                              }
  242. {                                                                            }
  243. { IN:       CX    = Number of samples.                                       }
  244. {           BX    = Offset of the channel data (TModRawChan).                }
  245. {           DI    = Offset of the buffer to be filled.                       }
  246. {                                                                            }
  247. { OUT:      The buffer will have been filled.                                }
  248. {                                                                            }
  249. { MODIFIES: Every register except DS.                                        }
  250. {                                                                            }
  251. {............................................................................}
  252.  
  253. PROCEDURE UnCanal; ASSEMBLER;
  254.   ASM
  255.         TEST    [TModRawChan(DS:BX).Flags],rcfActiveChannel  { ¿Active channel? }
  256.         JZ      @@Desactivado                                { If not -> do the silent loop }
  257.  
  258.         TEST    [TModRawChan(DS:BX).Flags],rcfSampleFinished { ¿Already finished? }
  259.         JNZ     @@Desactivado                                { If it is -> do the silent loop }
  260.  
  261.         TEST    BYTE PTR [TModRawChan(DS:BX).Volume],$FF     { Volumen }
  262.         JZ      @@Desactivado
  263.  
  264.         PUSH    BX                                           { BX is saved for restoring data at the end }
  265.  
  266.         TEST    [TModRawChan(DS:BX).Flags],rcfDoesLoop       { ¿Does the sample have a loop? }
  267.         JZ      @@NoDoesLoop                                 { If not -> do the loop-less routine }
  268.  
  269. {
  270.  
  271.   Sample with a loop (it doesn't check the end of the sample).
  272.  
  273. }
  274.  
  275.         MOV     AX,[TModRawChan(DS:BX).LoopEnd]              
  276.         MOV     WORD PTR [CS:@@dlData2-2],AX                 { Puts the loop-end OFFSET in its instruction }
  277.  
  278.         MOV     AX,[TModRawChan(DS:BX).LoopLen]              
  279.         MOV     WORD PTR [CS:@@dlData3-2],AX                 { Puts the loop-size in its instruction }
  280.  
  281.         MOV     DL,BYTE PTR [TModRawChan(DS:BX).Volume]      { Volume }
  282.         MOV     AL,[TModRawChan(DS:BX).StepFrac]             { Increment fraction }
  283.         MOV     BP,[TModRawChan(DS:BX).StepInt]              { Increment integer  }
  284.  
  285.         MOV     AH,[TModRawChan(DS:BX).SplPosFrac]           { Position OFFSET }
  286.  
  287.         LDS     SI,DWORD PTR [TModRawChan(DS:BX).SplPosInt]  { Pointer to the next sample to be read }
  288.  
  289.         MOV     BX,AX   { ¡¡¡No tocar!!! (BX es el puntero al buffer) }
  290. {
  291.  
  292.       Bucle. Se entra con:
  293.         DL = Volumen
  294.         BL = Parte fraccionaria del incremento.
  295.         BP = Parte entera del incremento.
  296.         BH = Parte fraccionaria de la posición en el sample.
  297.         SI = Parte entera de la posición en el sample.
  298.         ES = Segmento del buffer.
  299.         DS = Segmento del sample.
  300.         DI = Buffer donde se almacenan las muestras.
  301.         CX = Número total de muestras a generar.
  302.  
  303. }
  304.  
  305. @@dlLoop:
  306.         MOV     AL,[SI]                                      { Leo la muestra correspondiente }
  307.         IMUL    DL                                           { Multiplico por el volumen }
  308.         MOV     [ES:DI],AX                                   { Lo meto en el buffer (Instrucción automodificada) }
  309.         ADD     DI,MaxChannels*2
  310. @@dlData1:
  311.  
  312.         ADD     BH,BL                                        { Añade el incremento fraccionario }
  313.         ADC     SI,BP                                        { Añade el incremento entero }
  314.         JC      @@dlSplLoop                                  { Carry -> Ha pasado el límite, seguro }
  315.                                                              { (máximo nº de muestras = 65520) }
  316. @@dlChkLoop:
  317.         CMP     SI,$1234                                     { CMP BP,[TModRawChan(DS:BX).LoopEnd] }
  318. @@dlData2:                                                   { ¿He llegado al pto. de retorno del loop? }
  319.         JB      @@dlNoLoop
  320.  
  321. @@dlSplLoop:
  322.         SUB     SI,$1234                                     { SUB BP,[TModRawChan(DS:BX).LoopLen] }
  323. @@dlData3:                                                   { Si es así, vuelvo para atrás. Esto es muy importante hacerlo }
  324.                                                              { restando el tamaño del bucle, y conservando la parte frac. }
  325.  
  326. @@dlNoLoop:
  327.         LOOP    @@dlLoop                                     { Y fin del bucle }
  328.  
  329.         JMP     @@Finish                                     { Salta al final, donde se almacenan los valores de por donde }
  330.                                                              { han quedado los punteros y demás }
  331.  
  332. {
  333.  
  334.   Sample sin loop (no comprueba el fin de loop).
  335.   Filosofía igual al anterior.
  336.  
  337. }
  338.  
  339. @@NoDoesLoop:
  340.         MOV     AX,[TModRawChan(DS:BX).SplLimit]             { Pone el OFFSET del fin del sample en la instrucción }
  341.         MOV     WORD PTR [CS:@@nlData2-2],AX
  342.  
  343.         MOV     DL,BYTE PTR [TModRawChan(DS:BX).Volume]      { Volumen }
  344.         MOV     AL,[TModRawChan(DS:BX).StepFrac]             { Parte fraccionaria del incremento }
  345.         MOV     AH,[TModRawChan(DS:BX).SplPosFrac]           { Parte fraccionaria del OFFSET del puntero a la muestra }
  346.  
  347.         MOV     BP,[TModRawChan(DS:BX).StepInt]              { Parte entera del incremento }
  348.  
  349.         LDS     SI,DWORD PTR [TModRawChan(DS:BX).SplPosInt]  { Puntero a la próxima muestra a leer }
  350.  
  351.         MOV     BX,AX   { ¡¡¡No tocar!!! (BX es el puntero al buffer) }
  352.  
  353. {
  354.  
  355.       Bucle. Se entra con:
  356.         DL = Volumen
  357.         BL = Parte fraccionaria del incremento.
  358.         BP = Parte entera del incremento.
  359.         BH = Parte fraccionaria de la posición en el sample.
  360.         SI = Parte entera de la posición en el sample.
  361.         ES = Segmento del buffer.
  362.         DS = Segmento del sample.
  363.         DI = Buffer donde se almacenan las muestras.
  364.         CX = Número total de muestras a generar.
  365.  
  366. }
  367.  
  368. @@nlLoop:
  369.         MOV     AL,[SI]                                      { Leo la muestra correspondiente }
  370.         IMUL    DL                                           { Multiplico por el volumen }
  371.         MOV     [ES:DI],AX                                   { Lo meto en el buffer }
  372.         ADD     DI,MaxChannels*2
  373. @@nlData1:
  374.  
  375.         ADD     BH,BL                                        { Añade el incremento fraccionario }
  376.         ADC     SI,BP                                        { Añade el incremento entero }
  377.         JC      @@nlSeguroFin                                { Carry -> Ha pasado el límite del sample, seguro }
  378.                                                              { (máximo nº de muestras = 65520) }
  379.  
  380.         CMP     SI,$1234                                     { CMP BP,[TModRawChan(DS:BX).SplLimit] }
  381. @@nlData2:                                                   { ¿He llegado al final del sample? }
  382.         JNB     @@nlSeguroFin                                { Si es así, dejo de calcular }
  383.  
  384. @@nlNoLoop:
  385.         LOOP    @@nlLoop                                     { Y fin del bucle }
  386.  
  387.         JMP     @@Finish                                     { Salta al final, donde se almacenan los valores de por donde }
  388.                                                              { han quedado los punteros y demás }
  389.  
  390. @@nlSeguroFin:                                               { Se ha terminado el sample }
  391.         MOV     BX,SEG @Data                                 { Reinicializamos DS }
  392.         MOV     DS,BX
  393.         POP     BX                                           { Recupera el TModRawChan en BX }
  394.         OR      BYTE PTR [TModRawChan(DS:BX).Flags],rcfSampleFinished                { Desactivo el canal }
  395.         DEC     CX                                           { Decrementa el número de muestras, no se ha podido hacer antes }
  396.         JCXZ    @@Fin                                        { Si ya no hay más -> bye }
  397.  
  398. {
  399.  
  400.   Bucle correspondiente a un sample vacío. No se puede eliminar
  401.   porque tiene que, por lo menos, poner el buffer a cero.
  402.  
  403. }
  404.  
  405. @@Desactivado:
  406.         XOR     AX,AX                                        { Todas las muestras a cero }
  407. @@Data2:
  408.         MOV     [ES:DI],AX                                   { Le meto el cero en el buffer }
  409.         ADD     DI,MaxChannels*2
  410. @@Data1:
  411.         LOOP    @@Data2                                      { Fin del bucle }
  412.  
  413.         JMP     @@Fin                                        { Y me vuelvo sin restaurar nada }
  414.  
  415.  
  416.  
  417.  
  418.  
  419.  
  420.  
  421. @@Finish:
  422.         MOV     BP,SEG @Data                                 { Reinicializamos DS }
  423.         MOV     DS,BP
  424.         POP     BP                                           { Recupero el TModRawChan }
  425.         MOV     [TModRawChan(DS:BP).SplPosInt],SI            { Y guardo el OFFSET del sample donde se ha quedado }
  426.         MOV     [TModRawChan(DS:BP).SplPosFrac],BH
  427.  
  428. @@Fin:
  429.         MOV     AX,SEG @Data                                 { Reinicializamos DS }
  430.         MOV     DS,AX
  431.   END;
  432.  
  433.  
  434.  
  435.  
  436. {----------------------------------------------------------------------------}
  437. { Rutinas que se dedican a interpretar la partitura.                         }
  438. {____________________________________________________________________________}
  439.  
  440.  
  441.  
  442.  
  443. {----------------------------------------------------------------------------}
  444. {                                                                            }
  445. { RUTINA: SetNewSample                                                       }
  446. {                                                                            }
  447. { Inicializa un nuevo sample en uno de los canales.                          }
  448. {                                                                            }
  449. { ENTRADAS: Raw : TModRawChan correspondiente al canal.                      }
  450. {           Spl : TSample correspondinte al canal.                           }
  451. {                                                                            }
  452. { SALIDAS:  Ninguna.                                                         }
  453. {                                                                            }
  454. {............................................................................}
  455.  
  456. PROCEDURE SetNewSample(VAR Raw: TModRawChan; Spl: PInstrumentRec);
  457.   CONST
  458.     _or : BYTE    = 0;
  459.     f   : BOOLEAN = FALSE;
  460.   BEGIN
  461.     FillChar(Raw, SizeOf(Raw), 0);
  462.  
  463.     IF Spl = NIL THEN EXIT;
  464.  
  465.     ASM
  466.  
  467.         MOV     DI,WORD PTR Raw
  468.         LES     SI,Spl
  469.  
  470.         MOV     AX,WORD PTR TInstrumentRec([ES:SI]).data
  471.         MOV     TModRawChan([DI]).SplOfs1,AX
  472.         MOV     AX,WORD PTR TInstrumentRec([ES:SI+2]).data
  473.         MOV     TModRawChan([DI]).SplSeg1,AX                              { Inicializa los valores mínimos }
  474.         MOV     _or,rcfActiveChannel
  475.  
  476.         MOV     AX,WORD PTR TInstrumentRec([ES:SI+1]).repl
  477.         AND     AX,AX
  478.         JNZ     @@1
  479.          MOV    AL,BYTE PTR TInstrumentRec([ES:SI]).repl
  480.          CMP    AL,4
  481.          JNB    @@1
  482.          MOV    f,1
  483.          JMP    @@2
  484. @@1:    MOV     f,0                                     { Si tiene loop (no sé si es buena la comprobación }
  485.         OR      _or,rcfDoesLoop
  486. @@2:
  487.  
  488.       END;
  489.  
  490. (*
  491.     Raw.SplOfs1   := OFS(Spl^.data^);
  492.     Raw.SplSeg1   := SEG(Spl^.data^);                                      { Inicializa los valores mínimos }
  493.     _or           := rcfActiveChannel;
  494.  
  495.     f := Spl^.repl <= 4;
  496.     IF NOT f THEN INC(_or, rcfDoesLoop);                { Si tiene loop (no sé si es buena la comprobación }
  497. *)
  498.  
  499.     IF Spl^.len > MaxSample THEN BEGIN
  500.  
  501.       ASM
  502.  
  503.         MOV     DI,WORD PTR Raw                  { Entra aquí si es un sample largo (mayor de 65520 bytes) }
  504.         LES     SI,Spl
  505.  
  506.         OR      _or,rcfLongSample
  507.         MOV     TModRawChan([DI]).SplLimit1,MaxSample
  508.  
  509.       END;
  510.  
  511. (*
  512.       INC(_or, rcfLongSample);                   { Entra aquí si es un sample largo (mayor de 65520 bytes) }
  513.  
  514.       Raw.SplLimit1 := MaxSample;
  515. *)
  516.       Raw.SplLimit2 := Spl^.len - MaxSample;                      { Inicializa valores para el sample largo }
  517.       Raw.SplOfs2   := OFS(Spl^.xtra^);
  518.       Raw.SplSeg2   := SEG(Spl^.xtra^);
  519.  
  520.       IF NOT f THEN BEGIN                                                   { Si hay loop, pequeño lío :-) }
  521.         IF (Spl^.reps > MaxSample) OR (Spl^.reps+Spl^.repl <= MaxSample) THEN
  522.           Raw.LoopLen := Spl^.repl
  523.         ELSE BEGIN
  524.           Raw.LoopLen := Spl^.repl - MaxSample;
  525.           INC(_or, rcfLongLoopLen);
  526.         END;
  527.         IF Spl^.reps+Spl^.repl <= MaxSample THEN
  528.           Raw.LoopEnd := Spl^.reps + Spl^.repl
  529.         ELSE BEGIN
  530.           Raw.LoopEnd := Spl^.reps + Spl^.repl - MaxSample;
  531.           INC(_or, rcfLongLoopEnd);
  532.         END;
  533.       END;
  534.     END ELSE BEGIN
  535.  
  536.       ASM
  537.  
  538.         MOV     DI,WORD PTR Raw                { Entra aquí si es un sample pequeño (menor de 65520 bytes) }
  539.         LES     SI,Spl
  540.  
  541.         MOV     AX,WORD PTR TInstrumentRec([ES:SI]).len
  542.         MOV     TModRawChan([DI]).SplLimit1,AX
  543.  
  544.         MOV     AL,f
  545.         AND     AL,AL
  546.         JNZ     @@1
  547.          MOV    AX,WORD PTR TInstrumentRec([ES:SI]).repl
  548.          MOV    TModRawChan([DI]).LoopLen,AX
  549.          ADD    AX,WORD PTR TInstrumentRec([ES:SI]).reps
  550.          MOV    TModRawChan([DI]).LoopEnd,AX
  551. @@1:
  552.  
  553.       END;
  554.  
  555. (*
  556.       Raw.SplLimit1 := Spl^.len;                { Entra aquí si es un sample pequeño (menor de 65520 bytes) }
  557.  
  558.       IF NOT f THEN BEGIN                                                                    { Si hay loop }
  559.         Raw.LoopEnd := Spl^.reps + Spl^.repl;
  560.         Raw.LoopLen := Spl^.repl;
  561.       END;
  562. *)
  563.     END;
  564.  
  565.     ASM
  566.  
  567.         MOV     DI,WORD PTR Raw                
  568.  
  569.         MOV     TModRawChan([DI]).SplPosFrac,0
  570.  
  571.         MOV     AX,TModRawChan([DI]).SplOfs1
  572.         MOV     TModRawChan([DI]).SplPosInt,AX
  573.         MOV     TModRawChan([DI]).SplOfs,AX
  574.  
  575.         MOV     AX,TModRawChan([DI]).SplSeg1
  576.         MOV     TModRawChan([DI]).SplPosSeg,AX
  577.         MOV     TModRawChan([DI]).SplSeg,AX
  578.  
  579.         MOV     AX,TModRawChan([DI]).SplLimit1
  580.         MOV     TModRawChan([DI]).SplLimit,AX
  581.  
  582.         MOV     AL,_or
  583.         MOV     TModRawChan([DI]).Flags,AL
  584.  
  585.     END;
  586. (*
  587.     Raw.SplPosFrac := 0;
  588.     Raw.SplPosInt  := Raw.SplOfs1;
  589.     Raw.SplPosSeg  := Raw.SplSeg1;
  590.  
  591.     Raw.SplOfs   := Raw.SplOfs1;
  592.     Raw.SplSeg   := Raw.SplSeg1;
  593.     Raw.SplLimit := Raw.SplLimit1;
  594.  
  595.     Raw.Flags  := _or;
  596. *)
  597.   END; 
  598.  
  599.  
  600.                       
  601.  
  602.  
  603.  
  604. PROCEDURE MyMove(VAR Src, Dest; Bytes: WORD); ASSEMBLER;
  605.   ASM
  606.                 PUSH    DS
  607.  
  608.                 LDS     SI,[Src]
  609.                 LES     DI,[Dest]
  610.                 MOV     CX,[Bytes]
  611.  
  612.                 CLD
  613.  
  614.                 AND     CX,CX
  615.                 JZ      @@Fin
  616.  
  617.                 TEST    SI,1
  618.                 JZ      @@nobeg
  619.                  MOVSB
  620.                  DEC    CX
  621.                  JZ     @@Fin
  622.  
  623. @@nobeg:        MOV     BX,CX
  624.                 SHR     CX,1
  625.                 REP MOVSW
  626.                 MOV     CX,BX
  627.                 AND     CL,1
  628.                 JZ      @@Fin
  629.  
  630.                 MOVSB
  631. @@Fin:
  632.                 POP     DS
  633.   END;
  634.  
  635.  
  636.  
  637.  
  638.  
  639.  
  640. {----------------------------------------------------------------------------}
  641. {                                                                            }
  642. { PROCEDIMIENTO: ProcessNewNote                                              }
  643. {                                                                            }
  644. { Calcula y procesa la siguiente nota de la partitura.                       }
  645. {                                                                            }
  646. { ENTRADAS: Ninguna.                                                         }
  647. {                                                                            }
  648. { SALIDAS:  Ninguna.                                                         }
  649. {                                                                            }
  650. {............................................................................}
  651.  
  652. PROCEDURE ProcessNewNote(VAR Song: TSong);
  653.   CONST
  654.     i    : WORD        = 0;
  655.     j    : WORD        = 0;
  656.     n    : TFullNote   = (Instrument:0);
  657.     can  : ^TCanal     = NIL;
  658.     Patt : PPattern    = NIL;
  659.   BEGIN
  660.  
  661. {    SetBorder($FF, 0, 0);}
  662.  
  663.     i := (NoteHd + 1) AND (NoteBuffSize - 1);
  664.     NoteProcessed := @NoteBuff[i];
  665.     MyMove(NoteBuff[NoteHd], NoteProcessed^, SIZEOF(NoteBuff[0]));
  666.     NoteHd := i;
  667.     WITH NoteProcessed^ DO BEGIN
  668.  
  669.       EoMod       := NextNote = $FFFF;
  670.  
  671.       IF EoMod THEN 
  672.         IF MyLoopMod THEN BEGIN
  673.           NextSeq := MyRepStart;
  674.  
  675.           IF NextSeq < MyFirstPattern THEN
  676.             NextSeq := MyFirstPattern;
  677.  
  678.           NextNote  := 1;
  679.           EoMod     := FALSE;
  680.         END ELSE BEGIN
  681.           Playing   := FALSE;
  682.           EXIT;
  683.         END;
  684.  
  685.       NotePlaying := NextNote;
  686.       SeqPlaying  := NextSeq;
  687.       Volume      := UserVols;
  688.  
  689.       Patt := Song.GetPatternSeq(SeqPlaying);
  690.  
  691.       IF NextNote < Patt^.Patt^.NNotes THEN
  692.         INC(NextNote)
  693.       ELSE BEGIN
  694.         INC(NextSeq);
  695.         IF NextSeq > MySongLen THEN NextNote := $FFFF
  696.                                ELSE NextNote := 1;
  697.       END;
  698.  
  699.       IF Song.GetPatternSequence(SeqPlaying) = 0 THEN
  700.         BEGIN
  701.           ModCommands.Tempo        := Song.InitialTempo;
  702.           ModCommands.BPMIncrement := Song.InitialBPM;
  703.  
  704.           FillChar(Canales, SIZEOF(Canales), 0);
  705.  
  706.           FOR i := 1 TO MaxChannels DO
  707.             WITH Canales[i] DO BEGIN
  708.               Note.Period     := 800;
  709.               Note.Instrument := 1;
  710.               Note.Command    := mcNone;
  711.               Period          := 800;
  712.             END;
  713.  
  714.           REPEAT
  715.             INC(SeqPlaying);
  716.             IF SeqPlaying > MySongLen THEN NextNote := $FFFF
  717.                                       ELSE NextNote := 2;
  718.           UNTIL (NextNote = $FFFF) OR (Song.GetPatternSequence(SeqPlaying) <> 0);
  719.  
  720.           NextSeq := SeqPlaying;
  721.         END;
  722.  
  723.       IF (NotePlaying = 1) AND (Song.GetPatternTempo(SeqPlaying) <> 0) THEN
  724.         ModCommands.Tempo := Song.GetPatternTempo(SeqPlaying);
  725.  
  726.       FOR j := 1 TO Song.NumChannels DO BEGIN
  727.  
  728.         can := @Canales[j];
  729.  
  730.         Song.GetNote(SeqPlaying, NotePlaying, j, n);
  731.  
  732.         MyMove(n, Note[j], SIZEOF(n));
  733.  
  734.         IF ((n.Instrument <> 0)                     AND
  735.             (can^.Note.Instrument <> n.Instrument)) OR
  736.            ((0 <> n.Period)                         AND
  737.             (n.Command <> mcNPortamento)            AND
  738.             (n.Command <> mcT_VSlide))             THEN
  739.           BEGIN
  740.             IF n.Instrument <> 0 THEN
  741.               BEGIN
  742.                 can^.Note.Instrument := n.Instrument;
  743.                 can^.Instrument       := PInstrument(Song.GetInstrument(n.Instrument))^.Instr;
  744.               END;
  745.  
  746.             SetNewSample(RawChannels[j], can^.Instrument);
  747.           END;
  748.  
  749.         IF (n.Instrument <> 0) AND (can^.Instrument <> NIL) THEN
  750.           can^.Volume := can^.Instrument^.Vol;
  751.  
  752.         IF n.Volume <> 0 THEN
  753.           can^.Volume := n.Volume - 1;
  754.  
  755.         IF can^.Volume > 64 THEN can^.Volume := 64;
  756.  
  757.         CommandStart(Song, can^, n);
  758.  
  759.         NoteProcessed^.Tempo := ModCommands.Tempo;
  760.  
  761.       END;
  762.  
  763.       MuestrasPerTick := ActualHz DIV TicksPerSecond;
  764.  
  765.       IF MuestrasPerTick > MaxSplPerTick THEN
  766.         MuestrasPerTick := MaxSplPerTick;
  767.  
  768.       NMuestras       := MuestrasPerTick * Tempo;
  769.       NoteHz          := ActualHz;
  770.  
  771.     END;
  772.  
  773.     NoteTl    := NoteHd;
  774.     NoteSound := NoteProcessed;
  775.  
  776. {    SetBorder(0, 0, 0);}
  777.  
  778.   END;
  779.  
  780.  
  781.  
  782.  
  783. PROCEDURE FillChannels(VAR Song: TSong);
  784.   CONST
  785.     FirstTick : BOOLEAN       = TRUE;
  786.     i         : WORD          = 0;
  787.     p         : ^TModRawChan  = NIL;
  788.     q         : POINTER       = NIL;
  789.     Buf       : PSampleBuffer = NIL;
  790.   BEGIN
  791. {
  792.     SetBorder($FF, $FF, 0);
  793. }
  794.     Buf := @Buffers[BuffIdx];
  795.  
  796.     DelaySamples := Buf^.InUse;
  797.     IF DelaySamples THEN
  798.       BEGIN
  799.         EXIT;
  800.       END;
  801.  
  802.  
  803.     FOR i := 1 TO Song.NumChannels DO BEGIN
  804.       p := @RawChannels[i];
  805.       q := Addr(Buf^.IData^[i-1]);
  806.       ASM
  807.         PUSH    BP
  808.         PUSH    DI
  809.         PUSH    SI
  810.         PUSH    ES
  811.         MOV     CX,MuestrasPerTick
  812.         MOV     BX,WORD PTR p
  813.         LES     DI,q
  814.         CALL    UnCanal
  815.         POP     ES
  816.         POP     SI
  817.         POP     DI
  818.         POP     BP
  819.       END;
  820.  
  821.       SplBuf[i] := FilterChunkWord(q^, MuestrasPerTick, MaxChannels, FilterVal, SplBuf[i]);
  822.     END;
  823.  
  824.     Buf^.InUse    := TRUE;
  825.     Buf^.NSamples := MuestrasPerTick;
  826.     Buf^.RateHz   := NoteHz;
  827.     Buf^.DataType := dtInteger;
  828.     Buf^.Channels := MaxChannels;
  829.  
  830.     INC(BuffIdx);
  831.     IF BuffIdx > NumBuffers THEN BuffIdx := 1;
  832.  
  833.   END; { PROCEDURE FillChannels }
  834.  
  835. {----------------------------------------------------------------------------}
  836. {                                                                            }
  837. { PROCEDIMIENTO: ProcessTick                                                 }
  838. {                                                                            }
  839. { Procesa un tick de la música. Normalmente, se usan 50 ticks por segundo,   }
  840. { pero puede cambiarse.                                                      }
  841. {                                                                            }
  842. { ENTRADAS: Ninguna.                                                         }
  843. {                                                                            }
  844. { SALIDAS:  Ninguna.                                                         }
  845. {                                                                            }
  846. {............................................................................}
  847.  
  848. PROCEDURE ProcessTick(VAR Song: TSong);
  849.   CONST
  850.     SOTCanal = SIZEOF(TCanal);
  851.     incr     : INTEGER     = 0;
  852.     OTempoCt : WORD        = 0;
  853.     Can      : PCanal      = NIL;
  854.     Raw      : PModRawChan = NIL;
  855.     NoteHzFreq : LONGINT   = 0;
  856.     i        : WORD        = 0;
  857.     j        : WORD        = 0;
  858.     step     : LONGINT     = 0;
  859.     FBCount  : WORD        = 0;
  860.     NumChannels : BYTE     = 0;
  861.   LABEL
  862.     Fin;
  863.   BEGIN 
  864.  
  865.     IF DelaySamples THEN BEGIN
  866.       FillChannels(Song);
  867.  
  868.       IF DelaySamples THEN GOTO Fin;
  869.     END;
  870.  
  871.     INC(TickCount);
  872.  
  873.     OTempoCt := TempoCt;
  874.     INC(BPMCount, BPMIncrement);
  875.     INC(TempoCt, BPMCount DIV BPMDivider);
  876.     IF TempoCt <> OTempoCt THEN
  877.       BPMCount := BPMCount MOD BPMDivider;
  878.  
  879.     IF TempoCt >= NoteProcessed^.Tempo THEN BEGIN
  880.       ProcessNewNote(Song);
  881.  
  882.       IF NOT Playing THEN GOTO Fin;
  883.       TempoCt := 0;
  884.     END;
  885.  
  886.     IF NOT MyCanFallBack THEN
  887.       PleaseFallBack := 0;
  888.  
  889.     IF PleaseFallBack > 0 THEN BEGIN
  890.       PleaseFallBack := 0;
  891.       i := ActualHz;
  892.       WHILE (i = ActualHz) AND (i <> ActiveDevice^.GetRealFreqProc(0)) DO
  893.         BEGIN
  894.           DEC(DesiredHz, 100);
  895.           i := ActiveDevice^.GetRealFreqProc(DesiredHz);
  896.         END;
  897.       ChangeSamplingRate(DesiredHz);
  898.     END;
  899.  
  900.     NumChannels := Song.NumChannels;
  901.  
  902.     IF (TempoCt > 0) OR Song.FirstTick THEN
  903.       ASM
  904.                 XOR     CH,CH
  905.                 MOV     CL,[NumChannels]
  906. @@lp:            PUSH   CX
  907.                  MOV    AL,CL
  908.                  DEC    AL
  909.                  MOV    BL,SOTCanal
  910.                  MUL    BL
  911.                  MOV    SI,OFFSET Canales
  912.                  ADD    SI,AX
  913.                  MOV    BL,TCanal([SI]).Note.Command
  914.                  ADD    BL,BL
  915.                  XOR    BH,BH
  916.                  CALL   DoTickCommand
  917.                  POP    CX
  918.                  LOOP   @@lp
  919.       END;
  920.  
  921.  
  922.     FOR i := 1 TO Song.NumChannels DO
  923.       BEGIN
  924.         Can := @Canales[i];
  925.         Raw := @RawChannels[i];
  926.  
  927.         IF NOT Permisos[i] THEN Raw^.Flags := Raw^.Flags AND NOT rcfActiveChannel
  928.                            ELSE Raw^.Flags := Raw^.Flags OR      rcfActiveChannel;
  929.  
  930.         Raw^.Volume := (Can^.Volume*WORD(UserVols[i]) SHR 4) DIV
  931.                        ((Song.NumChannels + 1) AND $FFFE);
  932.         IF Raw^.Volume >= $80 THEN Raw^.Volume := $7F;
  933.  
  934.         IF Can^.Period = 0 THEN Can^.Period := 1;
  935.         IF NoteHz      = 0 THEN NoteHz      := 1;
  936.  
  937.         IF Can^.Instrument <> NIL THEN
  938.           Can^.RealPeriod := (LONGINT(Can^.Period) * Can^.Instrument^.NAdj) DIV
  939.                              Can^.Instrument^.DAdj
  940.         ELSE
  941.           Can^.RealPeriod := Can^.Period;
  942.  
  943.         ASM
  944.  
  945.                 LES     DI,[Can]                      { LONGINT(NoteHzFreq) := }
  946.                 MOV     DX,TCanal([ES:DI]).RealPeriod {   WORD(Can^.Period) *  }
  947.                 MOV     AX,[NoteHz]                   {   WORD(NoteHz)         }
  948.                 MUL     DX
  949.                 MOV     WORD PTR [NoteHzFreq],AX
  950.                 MOV     WORD PTR [NoteHzFreq+2],DX
  951.  
  952.         END;
  953.  
  954.         step := (65536 * 14000) DIV NoteHzFreq;
  955.  
  956.         Raw^.StepFrac := LO(step);
  957.         Raw^.StepInt  := step SHR 8;
  958.  
  959.         IF FilterIsOn THEN FilterVal := FilterOn
  960.                       ELSE FilterVal := FilterOff;
  961.       END;
  962.  
  963.     FillChannels(Song);
  964.  
  965. Fin:
  966.   END;
  967.  
  968. {----------------------------------------------------------------------------}
  969. {                                                                            }
  970. { PROCEDIMIENTO: ProcessTickEntry                                            }
  971. {                                                                            }
  972. { Entrada desde ensamblador de ProcessTick.                                  }
  973. {                                                                            }
  974. { ENTRADAS: Ninguna.                                                         }
  975. {                                                                            }
  976. { SALIDAS:  Ninguna.                                                         }
  977. {                                                                            }
  978. {............................................................................}
  979.  
  980. PROCEDURE ProcessTickEntry;
  981.   CONST
  982.     Semaphor : BYTE        = 0;
  983.     _SS      : WORD        = 0;
  984.     _SP      : WORD        = 0;
  985.   LABEL
  986.     Fin1, Fin2;
  987.   BEGIN 
  988.  
  989.     IF NOT Playing THEN
  990.       BEGIN
  991.         TempoCT := 1;
  992.         GOTO Fin1;
  993.       END;
  994.  
  995.     IF Semaphor <> 0 THEN
  996.       GOTO Fin2;
  997.  
  998.     INC(Semaphor);
  999.  
  1000.     ASM
  1001.         MOV     [_SS],SS
  1002.         MOV     [_SP],SP
  1003.         MOV     AX,DS
  1004.         MOV     SS,AX
  1005.         MOV     SP,OFFSET PlayModStack + PlayModStackSize
  1006.     END;
  1007.  
  1008.     ProcessTick(PlayingSong^);
  1009.  
  1010.     ASM
  1011.         MOV     SS,[_SS]
  1012.         MOV     SP,[_SP]
  1013.     END;
  1014.  
  1015.     DEC(Semaphor);
  1016.  
  1017. Fin1:
  1018.  
  1019.     IF ModTickProcValid THEN 
  1020.       ModTickProc(PlayingSong^, TempoCt = 0);
  1021.  
  1022. Fin2:
  1023.   END;
  1024.  
  1025.  
  1026.  
  1027.  
  1028. FUNCTION IdleGiver : PSampleBuffer; FAR;
  1029.   BEGIN
  1030.     IdleGiver := NIL;
  1031.   END;
  1032.  
  1033.  
  1034. FUNCTION BufferGiver : PSampleBuffer; FAR;
  1035.   BEGIN
  1036.     BufferGiver := NIL;
  1037.     IF NOT Buffers[BuffGive].InUse THEN EXIT;
  1038.     BufferGiver := @Buffers[BuffGive];
  1039.     INC(BuffGive);
  1040.     IF BuffGive > NumBuffers THEN BuffGive := 1;
  1041.   END;
  1042.  
  1043.  
  1044.  
  1045.  
  1046. PROCEDURE FillWithSamples(VAR Buff; Size: WORD);
  1047.   CONST
  1048.     mBuff : PIntBuff = NIL;
  1049.   BEGIN
  1050.  
  1051.     mBuff := Buffers[BuffGive].IData;
  1052.  
  1053.     ASM
  1054.         PUSH    DS
  1055.  
  1056.         XOR     SI,SI
  1057.  
  1058.         MOV     CX,[Size]
  1059.         MOV     AX,[MuestrasPerTick]
  1060.         AND     AX,AX
  1061.         JZ      @@bien
  1062.         CMP     AX,CX
  1063.         JNC     @@bien
  1064.  
  1065.         SUB     CX,AX
  1066.         MOV     SI,CX
  1067.         MOV     CX,AX
  1068.  
  1069. @@bien: CLD
  1070.         MOV     DX,16
  1071.         LDS     BX,[mBuff]
  1072.         LES     DI,[Buff]
  1073.  
  1074. @@lp:
  1075.                  MOV    AX,[BX]
  1076.                  ADD    AX,[BX+6]
  1077.                  ADD    AX,[BX+8]
  1078.                  ADD    AX,[BX+14]
  1079.                  ADD    AX,[BX+16]
  1080.                  ADD    AX,[BX+22]
  1081.                  ADD    AX,[BX+24]
  1082.                  ADD    AX,[BX+30]
  1083.  
  1084.                  MOV    DX,[BX+2]
  1085.                  ADD    DX,[BX+4]
  1086.                  ADD    DX,[BX+10]
  1087.                  ADD    DX,[BX+12]
  1088.                  ADD    DX,[BX+18]
  1089.                  ADD    DX,[BX+20]
  1090.                  ADD    DX,[BX+26]
  1091.                  ADD    DX,[BX+28]
  1092.  
  1093.          ADD    AX,DX
  1094.  
  1095.          JNO    @@nooverf
  1096.          JS     @@posit
  1097.           MOV   AX,-32768
  1098.          JMP    @@nooverf
  1099. @@posit:  MOV   AX,32767
  1100. @@nooverf:
  1101.  
  1102.          ADD    BX,MaxChannels*2
  1103.          STOSW
  1104.          LOOP   @@lp
  1105.  
  1106.         AND     SI,SI
  1107.         JZ      @@Fin
  1108.  
  1109.         MOV     CX,SI
  1110.         XOR     AX,AX
  1111.         REP STOSW
  1112.  
  1113. @@Fin:  POP     DS
  1114.     END;
  1115.  
  1116.   END;
  1117.  
  1118.  
  1119.  
  1120.  
  1121. PROCEDURE PlayStart(VAR Song: TSong);
  1122.   VAR
  1123.     i, j : WORD;
  1124.   BEGIN
  1125.  
  1126.     ASM CLI END;
  1127.  
  1128.     PlayingSong := @Song;
  1129.  
  1130.     MyFirstPattern := FirstPattern;
  1131.     MyRepStart     := RepStart;
  1132.     MySongLen      := SongLen;
  1133.  
  1134.     IF MySongLen = 0 THEN MySongLen := Song.SequenceLength;
  1135.  
  1136.     IF MyFirstPattern = 0 THEN NextSeq := 1
  1137.                           ELSE NextSeq := MyFirstPattern;
  1138.  
  1139.     IF NextSeq > MySongLen THEN
  1140.       BEGIN
  1141.         ASM STI END;
  1142.         EXIT;
  1143.       END;
  1144.  
  1145.     IF (MyRepStart              =  0)         AND
  1146.        (Song.SequenceRepStart   <= MySongLen) AND
  1147.        (Song.SequenceRepStart   <> 0)         THEN
  1148.       MyRepStart    := Song.SequenceRepStart;
  1149.  
  1150.     MyLoopMod       := (TRUE{LoopMod} AND (MyRepStart <> 0)) OR ForceLoopMod;
  1151.     TempoCt         := 254;
  1152.     Tempo           := Song.InitialTempo;
  1153.     BPMIncrement    := Song.InitialBPM;
  1154.     TickCount       := 0;
  1155.     NextNote        := 1;
  1156.     DelaySamples    := FALSE;
  1157.     MuestrasPerTick := 1;
  1158.     MaxSplPerTick   := MaxOutputFreq DIV TicksPerSecond;
  1159.  
  1160.     IF MyRepStart < NextSeq THEN MyRepStart := NextSeq;
  1161.  
  1162.     WITH NoteBuff[0] DO BEGIN
  1163.       EoMod       := FALSE;
  1164.       Tempo       := 6;
  1165.       NotePlaying := 0;
  1166.       SeqPlaying  := 0;
  1167.       Volume      := UserVols;
  1168.       NMuestras   := 0;
  1169.     END;
  1170.  
  1171.     NoteHd        := 0;
  1172.     NoteTl        := 0;
  1173.     NoteSound     := @NoteBuff[0];
  1174.     NoteProcessed := @NoteBuff[0];
  1175.  
  1176.     FillChar(Canales, SIZEOF(Canales), 0);
  1177.  
  1178.     FOR i := 1 TO MaxChannels DO
  1179.       WITH Canales[i] DO BEGIN
  1180.         Note.Period     := 800;
  1181.         Note.Instrument := 1;
  1182.         Note.Command    := mcNone;
  1183.         Period          := 800;
  1184.       END;
  1185.  
  1186.     SizeOfABuffer := MaxSplPerTick*MaxChannels*2;
  1187.     FillChar(Buffers, SIZEOF(Buffers),  0);
  1188.     FOR i := 1 TO NumBuffers DO
  1189.       BEGIN
  1190.         FullHeap.HGetMem(POINTER(Buffers[i].IData), SizeOfABuffer);
  1191.         IF Buffers[i].IData = NIL THEN
  1192.           BEGIN
  1193.             Song.Status := msOutOfMemory;
  1194.             PlayStop;
  1195.             ASM STI END;
  1196.             EXIT;
  1197.           END;
  1198.         FillChar(Buffers[i].IData^, SizeOfABuffer,  0);
  1199.       END;
  1200.     BuffIdx  := 1;
  1201.     BuffGive := 1;
  1202.  
  1203.     FillChar(RawChannels, SIZEOF(RawChannels), 0);
  1204.  
  1205.     ChangeSamplingRate(DesiredHz);
  1206.  
  1207.     ASM STI END;
  1208.  
  1209.     SetBufferAsker(IdleGiver);
  1210.  
  1211.     MyCanFallBack  := FALSE;
  1212.     Playing        := TRUE;
  1213.  
  1214.     FOR i := 1 TO NumBuffers DO
  1215.       ProcessTickEntry;
  1216.  
  1217.     StartSampling;
  1218.  
  1219.     SetBufferAsker(BufferGiver);
  1220.  
  1221.     WHILE DeviceIdling DO;
  1222.  
  1223.     PleaseFallBack := 0;
  1224.     MyCanFallBack  := CanFallBack;
  1225.  
  1226.   END;
  1227.  
  1228.  
  1229.  
  1230.  
  1231. PROCEDURE ChangeSamplingRate(Hz: WORD);
  1232.   VAR
  1233.     MyHz : WORD;
  1234.   LABEL
  1235.     Otra;
  1236.   BEGIN
  1237. Otra:
  1238.     DesiredHz := Hz;
  1239.     MyHz      := ActiveDevice^.GetRealFreqProc(Hz);
  1240.  
  1241.     IF MyHz >  MaxSplPerTick * TicksPerSecond THEN
  1242.       BEGIN
  1243.         DEC(Hz, 100);
  1244.         GOTO Otra;
  1245.       END;
  1246.  
  1247.     IF MyHz < 1000 THEN
  1248.       BEGIN
  1249.         INC(Hz, 100);
  1250.         GOTO Otra;
  1251.       END;
  1252.  
  1253.     IF MyHz <> ActualHz THEN
  1254.       BEGIN
  1255.         ActualHz := MyHz;
  1256.         SetPeriodicProc(ProcessTickEntry, TicksPerSecond * 3 {DIV 2});
  1257.       END;
  1258.  
  1259.   END;
  1260.  
  1261.  
  1262.  
  1263.  
  1264. PROCEDURE PlayStop;
  1265.   VAR
  1266.     i : WORD;
  1267.   BEGIN
  1268.     Playing := FALSE;
  1269.  
  1270.     SetBufferAsker(IdleGiver);
  1271.  
  1272.     WHILE (NOT DeviceIdling) AND (NOT KbdKeyPressed) DO;
  1273.  
  1274.     FOR i := 1 TO NumBuffers DO
  1275.       FullHeap.HFreeMem(POINTER(Buffers[i].IData), SizeOfABuffer);
  1276.   END;
  1277.  
  1278.  
  1279.  
  1280.  
  1281. BEGIN
  1282.   Playing        := FALSE;
  1283.   LoopMod        := FALSE;
  1284.   ActualHz       := 0;
  1285.  
  1286.   IF FilterIsOn THEN FilterVal := FilterOn
  1287.                 ELSE FilterVal := FilterOff;
  1288.  
  1289.   FillChar(UserVols, SIZEOF(UserVols), 255);
  1290.   FillChar(Permisos, SIZEOF(Permisos), TRUE);
  1291.   FillChar(SplBuf,      SIZEOF(SplBuf),      0);
  1292. END.
  1293.